package com.huixuebao.common.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;

import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class RSASign {

    //用于保存公钥与私钥
    private static Map<Integer, String> keyMap = new HashMap<Integer, String>();


    // 使用RSA算法加密、SHA算法签名
    public static final String SIGN_ALGORITHMS = "SHA1WithRSA";

    public static void main(String[] args) throws Exception {
        String content = "study hard and make progress everyday";
        System.out.println("原文 :" + content);
        //生成密钥
        //KeyPair keyPair = getKeyPair();
//        RSAPublicKey publicKey = (RSAPublicKey)keyPair.getPublic();
//        RSAPrivateKey privateKey = (RSAPrivateKey)keyPair.getPrivate();
        genKeyPair();
        String sha1Sign = getSha1Sign(content, keyMap.get(1));
        System.out.println("rsa签名 :" + sha1Sign);
        boolean sha1Verifty = verifyWhenSha1Sign(content, sha1Sign, keyMap.get(0));
        System.out.println("数据是否正确 :" + sha1Verifty);
    }

    /**
     * 生成密钥对
     * @return 密钥对
     * @throws Exception
     */
    public static KeyPair getKeyPair() throws NoSuchAlgorithmException {
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
        //初始化密钥对生成器，密钥最小长度为512位
        keyGen.initialize(512);
        KeyPair keyPair = keyGen.generateKeyPair();
        return keyPair;
    }

    public static void genKeyPair() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
        // 初始化密钥对生成器，密钥大小为512位(密钥长度最小为512位)
        keyPairGen.initialize(512);
        // 生成一个密钥对，保存在keyPair中
        KeyPair keyPair = keyPairGen.generateKeyPair();
        //获得私钥
        RSAPrivateKey privateKey = (RSAPrivateKey)keyPair.getPrivate();
        //获得公钥
        RSAPublicKey publicKey = (RSAPublicKey)keyPair.getPublic();
        //得到公钥字符串
        String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
        //得到私钥字符串
        String privateKeyString = new String(Base64.encodeBase64(privateKey.getEncoded()));
        //将公钥和私钥保存到Map 0表示公钥 1表示私钥
        keyMap.put(0, publicKeyString);
        keyMap.put(1, privateKeyString);
        log.info("publicKeyString {}", publicKeyString);
        log.info("privateKeyString {}", privateKeyString);
    }

    /**
     * 用md5生成内容摘要，再用RSA的私钥加密，进而生成数字签名
     * @param content 原文
     * @param privateKey RSA私钥
     * @return 签名完成后的数据
     * @throws Exception
     */
    public static String getMd5Sign(String content, RSAPrivateKey privateKey) throws Exception {
        byte[] contentBytes = content.getBytes("utf-8");
        Signature signature = Signature.getInstance(SIGN_ALGORITHMS);
        signature.initSign(privateKey);
        signature.update(contentBytes);
        byte[] signs = signature.sign();
        return Base64.encodeBase64String(signs);
    }


    /**
     * 对于使用md5和RSA私钥生成的数字签名进行解签验证
     * @param content 数据签名
     * @param sign 内容
     * @param publicKey 公钥
     * @return 验证数据的正确性
     * @throws Exception
     */
    public static boolean verifyWhenMd5Sign(String content, String sign, RSAPublicKey publicKey) throws Exception {
        byte[] contentBytes = content.getBytes("utf-8");
        Signature signature = Signature.getInstance(SIGN_ALGORITHMS);
        signature.initVerify(publicKey);
        signature.update(contentBytes);
        return signature.verify(Base64.decodeBase64(sign));
    }

    /**
     * 用sha1算法生成内容摘要，再用RSA的私钥加密，进而生成数字签名
     * @param content 原文
     * @param privateKey 私钥
     * @return 数字签名之后的数据
     * @throws Exception
     */
    public static String getSha1Sign(String content, String privateKey) throws Exception {
        //编码规则
        byte[] contentBytes = content.getBytes("utf-8");
        //数字签名
        Signature signature = Signature.getInstance(SIGN_ALGORITHMS);
        RSAPrivateKey key = (RSAPrivateKey) KeyFactory.getInstance("RSA")
                .generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
        signature.initSign(key);
        signature.update(contentBytes);
        byte[] signs = signature.sign();
        return Base64.encodeBase64String(signs);
    }

    /**
     * 对于使用shal和RSA私钥生成的数字签名进行解密验证
     * @param content   原文
     * @param sign 加密后的数字签名
     * @param publicKey 公钥
     * @return 数据的正确与否
     * @throws Exception
     */
    public static boolean verifyWhenSha1Sign(String content, String sign, String publicKey) throws Exception {
        byte[] contentBytes = content.getBytes("utf-8");
        Signature signature = Signature.getInstance(SIGN_ALGORITHMS);
        RSAPublicKey key = (RSAPublicKey) KeyFactory.getInstance("RSA")
                .generatePublic(new X509EncodedKeySpec(Base64.decodeBase64(publicKey)));
        signature.initVerify(key);
        signature.update(contentBytes);
        return signature.verify(Base64.decodeBase64(sign));
    }
}
